⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 im.c

📁 oscar message protocol stack
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * 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. * *       Need to rename all the mpmsg messages to aim_im_bleh. * *       Make sure aim_conn_findbygroup is used by all functions. */#define FAIM_INTERNAL#include <aim.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 ch 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(aim_bstream_t *bs, const fu8_t *c, fu16_t ch, const char *sn){	aimbs_putraw(bs, c, 8);	aimbs_put16(bs, ch);	aimbs_put8(bs, strlen(sn));	aimbs_putraw(bs, sn, strlen(sn));	return 8+2+1+strlen(sn);}/* * 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. * */faim_export fu16_t aim_im_fingerprint(const fu8_t *msghdr, int len){	static const struct {		fu16_t clientid;		int len;		fu8_t 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}	};	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. *  */faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params){	aim_conn_t *conn;	aim_frame_t *fr;	aim_snacid_t snacid;	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))		return -EINVAL;	if (!params)		return -EINVAL;	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+16)))		return -ENOMEM;	snacid = aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0);	aim_putsnac(&fr->data, 0x0004, 0x0002, 0x0000, snacid);	/* This is read-only (see Parameter Reply). Must be set to zero here. */	aimbs_put16(&fr->data, 0x0000);	/* These are all read-write */	aimbs_put32(&fr->data, params->flags); 	aimbs_put16(&fr->data, params->maxmsglen);	aimbs_put16(&fr->data, params->maxsenderwarn); 	aimbs_put16(&fr->data, params->maxrecverwarn); 	aimbs_put32(&fr->data, params->minmsginterval);	aim_tx_enqueue(sess, fr);	return 0;}/** * Subtype 0x0004 - Request ICBM parameter information. * */faim_export int aim_im_reqparams(aim_session_t *sess){	aim_conn_t *conn;	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))		return -EINVAL;	return aim_genericreq_n_snacid(sess, conn, 0x0004, 0x0004);}/** * Subtype 0x0005 - Receive parameter information. * */static int aim_im_paraminfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs){	aim_rxcallback_t userfunc;	struct aim_icbmparameters params;	params.maxchan = aimbs_get16(bs);	params.flags = aimbs_get32(bs);	params.maxmsglen = aimbs_get16(bs);	params.maxsenderwarn = aimbs_get16(bs);	params.maxrecverwarn = aimbs_get16(bs);	params.minmsginterval = aimbs_get32(bs);		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))		return userfunc(sess, rx, &params);	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 &#2026; 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 * */faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args){	aim_conn_t *conn;	aim_frame_t *fr;	aim_snacid_t snacid;	fu8_t ck[8];	int i, msgtlvlen;	static const fu8_t deffeatures[] = { 0x01, 0x01, 0x01, 0x02 };	if (!sess || !(conn = aim_conn_findbygroup(sess, 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;	}	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, msgtlvlen+128)))		return -ENOMEM;	/* XXX - should be optional */		snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1);	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);	/* 	 * 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++)		ck[i] = (fu8_t)rand();	/* ICBM header */	aim_im_puticbm(&fr->data, ck, 0x0001, args->destsn);	/* Message TLV (type 0x0002) */	aimbs_put16(&fr->data, 0x0002);	aimbs_put16(&fr->data, msgtlvlen);	/* Features TLV (type 0x0501) */	aimbs_put16(&fr->data, 0x0501);	if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) {		aimbs_put16(&fr->data, args->featureslen);		aimbs_putraw(&fr->data, args->features, args->featureslen);	} else {		aimbs_put16(&fr->data, sizeof(deffeatures));		aimbs_putraw(&fr->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) {			aimbs_put16(&fr->data, 0x0101);			aimbs_put16(&fr->data, sec->datalen + 4);			aimbs_put16(&fr->data, sec->charset);			aimbs_put16(&fr->data, sec->charsubset);			aimbs_putraw(&fr->data, sec->data, sec->datalen);		}	} else {		/* Insert message text in a TLV (type 0x0101) */		aimbs_put16(&fr->data, 0x0101);		/* Message block length */		aimbs_put16(&fr->data, args->msglen + 0x04);		/* Character set */		aimbs_put16(&fr->data, args->charset);		aimbs_put16(&fr->data, args->charsubset);		/* Message.  Not terminated */		aimbs_putraw(&fr->data, args->msg, args->msglen);	}	/* Set the Autoresponse flag */	if (args->flags & AIM_IMFLAGS_AWAY) {		aimbs_put16(&fr->data, 0x0004);		aimbs_put16(&fr->data, 0x0000);	} else if (args->flags & AIM_IMFLAGS_ACK) {		/* Set the Request Acknowledge flag */		aimbs_put16(&fr->data, 0x0003);		aimbs_put16(&fr->data, 0x0000);	}	if (args->flags & AIM_IMFLAGS_OFFLINE) {		aimbs_put16(&fr->data, 0x0006);		aimbs_put16(&fr->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) {		aimbs_put16(&fr->data, 0x0008);		aimbs_put16(&fr->data, 0x000c);		aimbs_put32(&fr->data, args->iconlen);		aimbs_put16(&fr->data, 0x0001);		aimbs_put16(&fr->data, args->iconsum);		aimbs_put32(&fr->data, args->iconstamp);	}	/*	 * Set the Buddy Icon Requested flag.	 * XXX - Every time?  Surely not...	 */	if (args->flags & AIM_IMFLAGS_BUDDYREQ) {		aimbs_put16(&fr->data, 0x0009);		aimbs_put16(&fr->data, 0x0000);	}	aim_tx_enqueue(sess, fr);	/* clean out SNACs over 60sec old */	aim_cleansnacs(sess, 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(). * */faim_export int aim_im_sendch1(aim_session_t *sess, const char *sn, fu16_t 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 */	args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART);	return aim_im_sendch1_ext(sess, &args);}/* * Subtype 0x0006 - Send a chat invitation. */faim_export int aim_im_sendch2_chatinvite(aim_session_t *sess, const char *sn, const char *msg, fu16_t exchange, const char *roomname, fu16_t instance){	aim_conn_t *conn;	aim_frame_t *fr;	aim_snacid_t snacid;	int i;	aim_msgcookie_t *cookie;	struct aim_invite_priv *priv;	fu8_t ck[8];	aim_tlvlist_t *otl = NULL, *itl = NULL;	fu8_t *hdr;	int hdrlen;	aim_bstream_t hdrbs;		if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))		return -EINVAL;	if (!sn || !msg || !roomname)		return -EINVAL;	for (i = 0; i < 8; i++)		ck[i] = (fu8_t)rand();	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152+strlen(sn)+strlen(roomname)+strlen(msg))))		return -ENOMEM;	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1);	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);	/* XXX should be uncached by an unwritten 'invite accept' handler */	if ((priv = malloc(sizeof(struct aim_invite_priv)))) {		priv->sn = strdup(sn);		priv->roomname = strdup(roomname);		priv->exchange = exchange;		priv->instance = instance;	}	if ((cookie = aim_mkcookie(ck, AIM_COOKIETYPE_INVITE, priv)))		aim_cachecookie(sess, cookie);	else		free(priv);	/* ICBM Header */	aimbs_putraw(&fr->data, ck, 8); /* Cookie */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -