changeset 30824:5661f30d1b8e

Got rid of receiving multipart messages over channel 1, which simplified the code immensely. No client sends them these days, so this is safe. In addition to that, I moved decoding logic from oscar.c to family_icbm.c. Also, removed family_odir stuff which I forgot to remove earlier and which resulted in liboscar failing to load. :(
author ivan.komarov@soc.pidgin.im
date Wed, 28 Jul 2010 23:11:14 +0000
parents 553cd883a29a
children a4f579485ce6
files libpurple/protocols/oscar/encoding.c libpurple/protocols/oscar/encoding.h libpurple/protocols/oscar/family_icbm.c libpurple/protocols/oscar/odc.c libpurple/protocols/oscar/oscar.c libpurple/protocols/oscar/oscar.h libpurple/protocols/oscar/oscar_data.c
diffstat 7 files changed, 38 insertions(+), 399 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/oscar/encoding.c	Wed Jul 28 19:37:26 2010 +0000
+++ b/libpurple/protocols/oscar/encoding.c	Wed Jul 28 23:11:14 2010 +0000
@@ -145,7 +145,7 @@
 }
 
 gchar *
-oscar_decode_im_part(PurpleAccount *account, const char *sourcebn, guint16 charset, guint16 charsubset, const gchar *data, gsize datalen)
+oscar_decode_im(PurpleAccount *account, const char *sourcebn, guint16 charset, const gchar *data, gsize datalen)
 {
 	gchar *ret = NULL;
 	/* charsetstr1 is always set to what the correct encoding should be. */
@@ -179,8 +179,8 @@
 		charsetstr2 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
 	}
 
-	purple_debug_info("oscar", "Parsing IM part, charset=0x%04hx, charsubset=0x%04hx, datalen=%" G_GSIZE_FORMAT ", choice1=%s, choice2=%s, choice3=%s\n",
-					  charset, charsubset, datalen, charsetstr1, charsetstr2, (charsetstr3 ? charsetstr3 : ""));
+	purple_debug_info("oscar", "Parsing IM, charset=0x%04hx, datalen=%" G_GSIZE_FORMAT ", choice1=%s, choice2=%s, choice3=%s\n",
+					  charset, datalen, charsetstr1, charsetstr2, (charsetstr3 ? charsetstr3 : ""));
 
 	ret = oscar_convert_to_utf8(data, datalen, charsetstr1, FALSE);
 	if (ret == NULL) {
--- a/libpurple/protocols/oscar/encoding.h	Wed Jul 28 19:37:26 2010 +0000
+++ b/libpurple/protocols/oscar/encoding.h	Wed Jul 28 23:11:14 2010 +0000
@@ -41,10 +41,7 @@
  * that don't correctly send non-ASCII messages.  And if Purple isn't
  * able to deal with that crap, then people complain like banshees.
  */
-gchar * oscar_decode_im_part(PurpleAccount *account,
-			const char *sourcebn,
-			guint16 charset, guint16 charsubset,
-			const gchar *data, gsize datalen);
+gchar * oscar_decode_im(PurpleAccount *account, const char *sourcebn, guint16 charset, const gchar *data, gsize datalen);
 
 /**
  * Figure out what encoding to use when sending a given outgoing message.
--- a/libpurple/protocols/oscar/family_icbm.c	Wed Jul 28 19:37:26 2010 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Wed Jul 28 23:11:14 2010 +0000
@@ -44,6 +44,7 @@
  *       Make sure flap_connection_findbygroup is used by all functions.
  */
 
+#include "encoding.h"
 #include "oscar.h"
 #include "peer.h"
 
@@ -293,10 +294,6 @@
  * 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)
 {
@@ -915,223 +912,41 @@
 	byte_stream_destroy(&bs);
 }
 
-/*
- * Ahh, the joys of nearly ridiculous over-engineering.
- *
- * Not only do AIM ICBM's support multiple channels.  Not only do they
- * support multiple character sets.  But they support multiple character
- * sets / encodings within the same ICBM.
- *
- * These multipart messages allow for complex space savings techniques, which
- * seem utterly unnecessary by today's standards.  In fact, there is only
- * one client still in popular use that still uses this method: AOL for the
- * Macintosh, Version 5.0.  Obscure, yes, I know.
- *
- * In modern (non-"legacy") clients, if the user tries to send a character
- * that is not ISO-8859-1 or ASCII, the client will send the entire message
- * as UNICODE, meaning that every character in the message will occupy the
- * full 16 bit UNICODE field, even if the high order byte would be zero.
- * Multipart messages prevent this wasted space by allowing the client to
- * only send the characters in UNICODE that need to be sent that way, and
- * the rest of the message can be sent in whatever the native character
- * set is (probably ASCII).
- *
- * An important note is that sections will be displayed in the order that
- * they appear in the ICBM.  There is no facility for merging or rearranging
- * sections at run time.  So if you have, say, ASCII then UNICODE then ASCII,
- * you must supply two ASCII sections with a UNICODE in the middle, and incur
- * the associated overhead.
- *
- * Normally I would have laughed and given a firm 'no' to supporting this
- * seldom-used feature, but something is attracting me to it.  In the future,
- * it may be possible to abuse this to send mixed-media messages to other
- * open source clients (like encryption or something) -- see faimtest for
- * examples of how to do this.
- *
- * I would definitely recommend avoiding this feature unless you really
- * know what you are doing, and/or you have something neat to do with it.
- *
- */
-int aim_mpmsg_init(OscarData *od, aim_mpmsg_t *mpm)
+static void
+incomingim_ch1_parsemsg(OscarData *od, aim_userinfo_t *userinfo, ByteStream *message, struct aim_incomingim_ch1_args *args)
 {
-
-	memset(mpm, 0, sizeof(aim_mpmsg_t));
-
-	return 0;
-}
+	PurpleAccount *account = purple_connection_get_account(od->gc);
+	/*
+	 * We're interested in the inner TLV 0x101, which contains precious, precious message.
+	 */
+	while (byte_stream_empty(message) >= 4) {
+		guint16 type = byte_stream_get16(message);
+		guint16 length = byte_stream_get16(message);
+		if (type == 0x101) {
+			gchar *msg;
+			guint16 msglen = length - 4; /* charset + charsubset */
+			guint16 charset = byte_stream_get16(message);
+			byte_stream_advance(message, 2); /* charsubset */
 
-static int mpmsg_addsection(OscarData *od, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, gchar *data, guint16 datalen)
-{
-	aim_mpmsg_section_t *sec;
-
-	sec = g_malloc(sizeof(aim_mpmsg_section_t));
-
-	sec->charset = charset;
-	sec->charsubset = charsubset;
-	sec->data = data;
-	sec->datalen = datalen;
-	sec->next = NULL;
-
-	if (!mpm->parts)
-		mpm->parts = sec;
-	else {
-		aim_mpmsg_section_t *cur;
-
-		for (cur = mpm->parts; cur->next; cur = cur->next)
-			;
-		cur->next = sec;
+			msg = byte_stream_getstr(message, msglen);
+			args->msg = oscar_decode_im(account, userinfo->bn, charset, msg, msglen);
+		} else {
+			byte_stream_advance(message, length);
+		}
 	}
-
-	mpm->numparts++;
-
-	return 0;
-}
-
-void aim_mpmsg_free(OscarData *od, aim_mpmsg_t *mpm)
-{
-	aim_mpmsg_section_t *cur;
-
-	for (cur = mpm->parts; cur; ) {
-		aim_mpmsg_section_t *tmp;
-
-		tmp = cur->next;
-		g_free(cur->data);
-		g_free(cur);
-		cur = tmp;
-	}
-
-	mpm->numparts = 0;
-	mpm->parts = NULL;
-
-	return;
 }
 
-/*
- * Start by building the multipart structures, then pick the first
- * human-readable section and stuff it into args->msg so no one gets
- * suspicious.
- */
-static int incomingim_ch1_parsemsgs(OscarData *od, aim_userinfo_t *userinfo, guint8 *data, int len, struct aim_incomingim_ch1_args *args)
+static int
+incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, ByteStream *bs, guint8 *cookie)
 {
-	/* Should this be ASCII -> UNICODE -> Custom */
-	static const guint16 charsetpri[] = {
-		AIM_CHARSET_ASCII, /* ASCII first */
-		AIM_CHARSET_LATIN_1, /* then ISO-8859-1 */
-		AIM_CHARSET_UNICODE, /* UNICODE as last resort */
-	};
-	static const int charsetpricount = 3;
-	int i;
-	ByteStream mbs;
-	aim_mpmsg_section_t *sec;
-
-	byte_stream_init(&mbs, data, len);
-
-	while (byte_stream_empty(&mbs)) {
-		guint16 msglen, flag1, flag2;
-		gchar *msgbuf;
-
-		byte_stream_get8(&mbs); /* 01 */
-		byte_stream_get8(&mbs); /* 01 */
-
-		/* Message string length, including character set info. */
-		msglen = byte_stream_get16(&mbs);
-		if (msglen > byte_stream_empty(&mbs))
-		{
-			purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->bn);
-			break;
-		}
-
-		/* Character set info */
-		flag1 = byte_stream_get16(&mbs);
-		flag2 = byte_stream_get16(&mbs);
-
-		/* Message. */
-		msglen -= 4;
-
-		/*
-		 * For now, we don't care what the encoding is.  Just copy
-		 * it into a multipart struct and deal with it later. However,
-		 * always pad the ending with a NULL.  This makes it easier
-		 * to treat ASCII sections as strings.  It won't matter for
-		 * UNICODE or binary data, as you should never read past
-		 * the specified data length, which will not include the pad.
-		 *
-		 * XXX - There's an API bug here.  For sending, the UNICODE is
-		 * given in host byte order (aim_mpmsg_addunicode), but here
-		 * the received messages are given in network byte order.
-		 *
-		 */
-		msgbuf = (gchar *)byte_stream_getraw(&mbs, msglen);
-		mpmsg_addsection(od, &args->mpmsg, flag1, flag2, msgbuf, msglen);
-
-	} /* while */
-
-	/*
-	 * Clients that support multiparts should never use args->msg, as it
-	 * will point to an arbitrary section.
-	 *
-	 * Here, we attempt to provide clients that do not support multipart
-	 * messages with something to look at -- hopefully a human-readable
-	 * string.  But, failing that, a UNICODE message, or nothing at all.
-	 *
-	 * Which means that even if args->msg is NULL, it does not mean the
-	 * message was blank.
-	 *
-	 */
-	for (i = 0; i < charsetpricount; i++) {
-		for (sec = args->mpmsg.parts; sec; sec = sec->next) {
-
-			if (sec->charset != charsetpri[i])
-				continue;
-
-			/* Great. We found one.  Fill it in. */
-			args->charset = sec->charset;
-			args->charsubset = sec->charsubset;
-
-			/* Set up the simple flags */
-			switch (args->charsubset)
-			{
-				case 0x0000:
-					/* standard subencoding? */
-					break;
-				case 0x000b:
-					args->icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH;
-					break;
-				case 0xffff:
-					/* no subencoding */
-					break;
-				default:
-					break;
-			}
-
-			args->msg = sec->data;
-			args->msglen = sec->datalen;
-
-			return 0;
-		}
-	}
-
-	/* No human-readable sections found.  Oh well. */
-	args->charset = args->charsubset = 0xffff;
-	args->msg = NULL;
-	args->msglen = 0;
-
-	return 0;
-}
-
-static int incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, ByteStream *bs, guint8 *cookie)
-{
-	guint16 type, length, magic1, msglen = 0;
+	guint16 type, length;
 	aim_rxcallback_t userfunc;
 	int ret = 0;
-	int rev = 0;
 	struct aim_incomingim_ch1_args args;
 	unsigned int endpos;
 
 	memset(&args, 0, sizeof(args));
 
-	aim_mpmsg_init(od, &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
@@ -1151,93 +966,20 @@
 		endpos = byte_stream_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
-			 *
-			 * Slick and possible others reverse 'Features' and 'Messages' section.
-			 * Thus, the TLV could have following layout:
-			 *   - 0101 -- Unknown (possibly magic for message section)
-			 *   - Message
-			 *   - 0501 -- Unknown (possibly magic for features section)
-			 *   - Features: Don't know how to interpret these
-			 */
-
-			magic1 = byte_stream_get16(bs); /* 0501 or 0101 */
-			if (magic1 == 0x101) /* Bad, message comes before attributes */
-			{
-				/* Jump to the features section */
-				msglen = byte_stream_get16(bs);
-				bs->offset += msglen;
-				rev = 1;
-
-				magic1 = byte_stream_get16(bs); /* 0501 */
-			}
-
-			if (magic1 != 0x501)
-			{
-				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->bn);
-				break;
-			}
-
-			args.featureslen = byte_stream_get16(bs);
-			if (args.featureslen > byte_stream_empty(bs))
-			{
-				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->bn);
-				break;
-			}
-			if (args.featureslen == 0)
-			{
-				args.features = NULL;
-			}
-			else
-			{
-				args.features = byte_stream_getraw(bs, args.featureslen);
-				args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES;
-			}
-
-			if (rev)
-			{
-				/* Fix buffer back to message */
-				bs->offset -= args.featureslen + 2 + 2 + msglen + 2 + 2;
-			}
-
-			magic1 = byte_stream_get16(bs); /* 01 01 */
-			if (magic1 != 0x101) /* Bad, message comes before attributes */
-			{
-				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->bn);
-				break;
-			}
-			msglen = byte_stream_get16(bs);
-
-			/*
-			 * The rest of the TLV contains one or more message
-			 * blocks...
-			 */
-			incomingim_ch1_parsemsgs(od, userinfo, bs->data + bs->offset - 2 - 2 /* XXX evil!!! */, msglen + 2 + 2, &args);
-
+			ByteStream tlv02;
+			byte_stream_init(&tlv02, bs->data + bs->offset, length);
+			incomingim_ch1_parsemsg(od, userinfo, &tlv02, &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. */
-
 			/*
 			 * This flag is set on incoming offline messages for both
 			 * AIM and ICQ accounts.
 			 */
 			args.icbmflags |= AIM_IMFLAGS_OFFLINE;
-
 		} else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */
-
 			args.iconlen = byte_stream_get32(bs);
 			byte_stream_get16(bs); /* 0x0001 */
 			args.iconsum = byte_stream_get16(bs);
@@ -1255,39 +997,16 @@
 			 */
 			if (args.iconlen)
 				args.icbmflags |= AIM_IMFLAGS_HASICON;
-
 		} else if (type == 0x0009) {
-
 			args.icbmflags |= AIM_IMFLAGS_BUDDYREQ;
-
 		} else if (type == 0x000b) { /* Non-direct connect typing notification */
-
 			args.icbmflags |= AIM_IMFLAGS_TYPINGNOT;
-
 		} else if (type == 0x0016) {
-
 			/*
 			 * UTC timestamp for when the message was sent.  Only
 			 * provided for offline messages.
 			 */
 			args.timestamp = byte_stream_get32(bs);
-
-		} else if (type == 0x0017) {
-
-			if (length > byte_stream_empty(bs))
-			{
-				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->bn);
-				break;
-			}
-			g_free(args.extdata);
-			args.extdatalen = length;
-			if (args.extdatalen == 0)
-				args.extdata = NULL;
-			else
-				args.extdata = byte_stream_getraw(bs, args.extdatalen);
-
-		} else {
-			purple_debug_misc("oscar", "incomingim_ch1: unknown TLV 0x%04x (len %d)\n", type, length);
 		}
 
 		/*
@@ -1305,10 +1024,7 @@
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 		ret = userfunc(od, conn, frame, channel, userinfo, &args);
 
-	aim_mpmsg_free(od, &args.mpmsg);
-	g_free(args.features);
-	g_free(args.extdata);
-
+	g_free(args.msg);
 	return ret;
 }
 
--- a/libpurple/protocols/oscar/odc.c	Wed Jul 28 19:37:26 2010 +0000
+++ b/libpurple/protocols/oscar/odc.c	Wed Jul 28 23:11:14 2010 +0000
@@ -367,8 +367,7 @@
 		g_datalist_clear(&attributes);
 
 		/* Append the message up to the tag */
-		utf8 = oscar_decode_im_part(account, conn->bn,
-				encoding, 0x0000, tmp, start - tmp);
+		utf8 = oscar_decode_im(account, conn->bn, encoding, tmp, start - tmp);
 		if (utf8 != NULL) {
 			g_string_append(newmsg, utf8);
 			g_free(utf8);
@@ -387,8 +386,7 @@
 	/* Append any remaining message data */
 	if (tmp <= msgend)
 	{
-		utf8 = oscar_decode_im_part(account, conn->bn,
-				encoding, 0x0000, tmp, msgend - tmp);
+		utf8 = oscar_decode_im(account, conn->bn, encoding, tmp, msgend - tmp);
 		if (utf8 != NULL) {
 			g_string_append(newmsg, utf8);
 			g_free(utf8);
--- a/libpurple/protocols/oscar/oscar.c	Wed Jul 28 19:37:26 2010 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Wed Jul 28 23:11:14 2010 +0000
@@ -1500,17 +1500,11 @@
 	PurpleMessageFlags flags = 0;
 	struct buddyinfo *bi;
 	PurpleStoredImage *img;
-	GString *message;
 	gchar *tmp;
-	aim_mpmsg_section_t *curpart;
 	const char *start, *end;
 	GData *attribs;
 
-	purple_debug_misc("oscar", "Received IM from %s with %d parts\n",
-					userinfo->bn, args->mpmsg.numparts);
-
-	if (args->mpmsg.numparts == 0)
-		return 1;
+	purple_debug_misc("oscar", "Received IM from %s\n", userinfo->bn);
 
 	bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
 	if (!bi) {
@@ -1550,19 +1544,7 @@
 	}
 	purple_imgstore_unref(img);
 
-	message = g_string_new("");
-	curpart = args->mpmsg.parts;
-	while (curpart != NULL) {
-		tmp = oscar_decode_im_part(account, userinfo->bn, curpart->charset,
-				curpart->charsubset, curpart->data, curpart->datalen);
-		if (tmp != NULL) {
-			g_string_append(message, tmp);
-			g_free(tmp);
-		}
-
-		curpart = curpart->next;
-	}
-	tmp = g_string_free(message, FALSE);
+	tmp = g_strdup(args->msg);
 
 	/*
 	 * Convert iChat color tags to normal font tags.
@@ -1646,8 +1628,7 @@
 		tmp = tmp2;
 	}
 
-	serv_got_im(gc, userinfo->bn, tmp, flags,
-			(args->icbmflags & AIM_IMFLAGS_OFFLINE) ? args->timestamp : time(NULL));
+	serv_got_im(gc, userinfo->bn, tmp, flags, (args->icbmflags & AIM_IMFLAGS_OFFLINE) ? args->timestamp : time(NULL));
 	g_free(tmp);
 
 	return 1;
@@ -1827,7 +1808,7 @@
 
 		purple_str_strip_char(msg1[i], '\r');
 		/* TODO: Should use an encoding other than ASCII? */
-		msg2[i] = oscar_decode_im_part(account, uin, AIM_CHARSET_ASCII, 0x0000, msg1[i], strlen(msg1[i]));
+		msg2[i] = oscar_decode_im(account, uin, AIM_CHARSET_ASCII, msg1[i], strlen(msg1[i]));
 		g_free(uin);
 	}
 	msg2[i] = NULL;
@@ -1884,7 +1865,7 @@
 				gchar *reason = NULL;
 
 				if (msg2[5] != NULL)
-					reason = oscar_decode_im_part(account, bn, AIM_CHARSET_LATIN_1, 0x0000, msg2[5], strlen(msg2[5]));
+					reason = oscar_decode_im(account, bn, AIM_CHARSET_LATIN_1, msg2[5], strlen(msg2[5]));
 
 				purple_debug_info("oscar",
 						   "Received an authorization request from UIN %u\n",
--- a/libpurple/protocols/oscar/oscar.h	Wed Jul 28 19:37:26 2010 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Wed Jul 28 23:11:14 2010 +0000
@@ -616,27 +616,6 @@
 #define AIM_CHARSET_LATIN_1 0x0003 /* ISO 8859-1 */
 
 /*
- * Multipart message structures.
- */
-typedef struct aim_mpmsg_section_s
-{
-	guint16 charset;
-	guint16 charsubset;
-	gchar *data;
-	guint16 datalen;
-	struct aim_mpmsg_section_s *next;
-} aim_mpmsg_section_t;
-
-typedef struct aim_mpmsg_s
-{
-	unsigned int numparts;
-	aim_mpmsg_section_t *parts;
-} aim_mpmsg_t;
-
-int aim_mpmsg_init(OscarData *od, aim_mpmsg_t *mpm);
-void aim_mpmsg_free(OscarData *od, aim_mpmsg_t *mpm);
-
-/*
  * Arguments to aim_send_im_ext().
  *
  * This is really complicated.  But immensely versatile.
@@ -663,50 +642,20 @@
 };
 
 /*
- * Arguments to aim_send_rtfmsg().
- */
-struct aim_sendrtfmsg_args
-{
-	const char *destbn;
-	guint32 fgcolor;
-	guint32 bgcolor;
-	const char *rtfmsg; /* must be in RTF */
-};
-
-/*
  * This information is provided in the Incoming ICBM callback for
  * Channel 1 ICBM's.
- *
- * Multipart messages require some explanation. If you want to use them,
- * I suggest you read all the comments in family_icbm.c.
- *
  */
 struct aim_incomingim_ch1_args
 {
-	/* Always provided */
-	aim_mpmsg_t mpmsg;
 	guint32 icbmflags; /* some flags apply only to ->msg, not all mpmsg */
 	time_t timestamp; /* Only set for offline messages */
 
-	/* Only provided if message has a human-readable section */
 	gchar *msg;
-	int msglen;
 
 	/* Only provided if AIM_IMFLAGS_HASICON is set */
 	time_t iconstamp;
 	guint32 iconlen;
 	guint16 iconsum;
-
-	/* Only provided if AIM_IMFLAGS_CUSTOMFEATURES is set */
-	guint8 *features;
-	guint8 featureslen;
-
-	guint8 extdatalen;
-	guint8 *extdata;
-
-	/* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set */
-	guint16 charset;
-	guint16 charsubset;
 };
 
 /* Valid values for channel 2 args->status */
@@ -1247,7 +1196,6 @@
 int locate_modfirst(OscarData *od, aim_module_t *mod);
 int service_modfirst(OscarData *od, aim_module_t *mod);
 int popups_modfirst(OscarData *od, aim_module_t *mod);
-int odir_modfirst(OscarData *od, aim_module_t *mod);
 int bart_modfirst(OscarData *od, aim_module_t *mod);
 int ssi_modfirst(OscarData *od, aim_module_t *mod);
 int icq_modfirst(OscarData *od, aim_module_t *mod);
--- a/libpurple/protocols/oscar/oscar_data.c	Wed Jul 28 19:37:26 2010 +0000
+++ b/libpurple/protocols/oscar/oscar_data.c	Wed Jul 28 23:11:14 2010 +0000
@@ -60,7 +60,6 @@
 	aim__registermodule(od, stats_modfirst);
 	aim__registermodule(od, chatnav_modfirst);
 	aim__registermodule(od, chat_modfirst);
-	aim__registermodule(od, odir_modfirst);
 	aim__registermodule(od, bart_modfirst);
 	/* missing 0x11 - 0x12 */
 	aim__registermodule(od, ssi_modfirst);